#pragma once
#include <unordered_map>

#include "crow_all.h"
#include "storage.h"
#include "libzqutil/ustring.h"

namespace libzq
{
	namespace http
	{
		template<typename Storage>
		struct Session :
			public crow::CookieParser,
			public Storage
		{
			struct context :
				public crow::CookieParser::context
			{
				std::unordered_map<std::string, std::string> session_data;
				bool session_deleted = false;
				const std::string& get(const std::string& key) const
				{
					auto data = session_data.find(key);
					if (data != session_data.end())
						return data->second;
					static std::string empty = "";
					return empty;
				}

				void set(const std::string& key, const std::string& val)
				{
					session_data[key] = val;
				}

				void remove(const std::string& key)
				{
					auto data = session_data.find(key);
					if (data != session_data.end()) session_data.erase(data);
				}

				void clear()
				{
					session_deleted = true;
					session_data.clear();
				}
			};

			template<typename... Args>
			static void init(Args... args)
			{
				Storage::init(args...);
			}

			void before_handle(crow::request& req, crow::response& res, context& ctx)
			{
				crow::CookieParser::before_handle(req, res, ctx);
				std::string session_id = ctx.get_cookie("sessionid");
				if (session_id.empty())
				{
					ctx.set_cookie("sessionid", make_uuid());
				}
				else
				{
					ctx.session_data = Storage::load(session_id);
				}
			}

			void after_handle(crow::request& req, crow::response& res, context& ctx)
			{
				std::string session_id = ctx.get_cookie("sessionid");
				if (ctx.session_deleted)
				{
					Storage::remove(session_id);
					ctx.set_expires(-1);
				}
				else
				{
					Storage::save(session_id, ctx.session_data);
				}
				crow::CookieParser::after_handle(req, res, ctx);
			}
		};

	}
}

using FileSession = libzq::http::Session<libzq::http::FileSessionStorage>;
using RedisSession = libzq::http::Session<libzq::http::RedisSessionStorage>;